import type {
  IGenerateCaptchaByUsername,
  IGenerateImageId,
} from '@/interfaces';
import {
  type ChangeEvent,
  type FormEvent,
  useContext,
  useEffect,
  useRef,
  useState,
} from 'react';
import type JSEncrypt from 'jsencrypt';
import useToast from '@/hooks/useToast';
import { useMutation, useQuery } from '@tanstack/react-query';
import {
  generateCaptchaByUsername,
  generateImageId,
  loginByUsername,
  queryPasswordPublicKey,
} from '@/services/api';
import dayjs from 'dayjs';
import classNames from 'classnames';
import Image from 'next/image';
import { useSearchParams } from 'next/navigation';
import { LoginPageContext } from '@/contexts/login';
import { AppContext } from '@/contexts/app';

export default function UsernameLogin() {
  const {
    source: { path: data },
    translatedFields,
  } = useContext(LoginPageContext)!;
  const appContext = useContext(AppContext);
  const metadata = appContext.metadata!;
  const env = metadata.env;
  const [form, setForm] = useState({
    username: '',
    password: '',
    captcha: '',
  });
  const [imageCaptchaId, setImageCaptchaId] = useState('');
  const [imageLoadFailure, setImageLoadFailure] = useState(false);
  const [usernameCaptchaConfig, setUsernameCaptchaConfig] = useState({
    ...data.imageConfig,
    isClick: false,
    countdown: 0,
    countdownId: undefined,
  });
  const [disableLogin, setDisableLogin] = useState(false);
  const captchaInputRef = useRef<HTMLInputElement>(null);
  const jsEncryptRef = useRef<JSEncrypt>();
  const urlSearchParams = useSearchParams();
  const { show } = useToast();

  useEffect(() => {
    const countdownId = usernameCaptchaConfig.countdownId;
    return () => {
      if (countdownId) {
        clearInterval(countdownId);
      }
    };
  }, [usernameCaptchaConfig.countdownId]);

  const generateImageIdQuery = useQuery(
    ['/captcha', '/image'],
    async () => {
      try {
        const imageData = (await generateImageId()) as IGenerateImageId;
        setImageCaptchaId(imageData.id);
        return imageData;
      } catch (e) {
        show({
          type: 'DANGER',
          message: e,
        });
        throw e;
      }
    },
    {
      enabled: !!(data.imageConfig && data.imageConfig.enable),
    },
  );

  const generateCaptchaByUsernameMutation = useMutation(
    generateCaptchaByUsername,
  );
  const loginByUsernameMutation = useMutation(loginByUsername);
  const queryPasswordPublicKeyMutation = useMutation(queryPasswordPublicKey);

  function onChangeForm(e: ChangeEvent<HTMLInputElement>) {
    const name = e.target.name;
    const value = e.target.value;
    setForm({ ...form, [name]: value.trim() });
  }

  async function onSubmit(e: FormEvent<HTMLFormElement>) {
    try {
      e.preventDefault();
      e.stopPropagation();

      const { username, password, captcha } = form;
      const imageConfig = data.imageConfig;
      if (!username) {
        show({
          type: 'DANGER',
          message: translatedFields.enterValidUsername,
        });
        return;
      } else if (!password) {
        show({
          type: 'DANGER',
          message: translatedFields.enterValidPassword,
        });
        return;
      } else if (imageConfig && imageConfig.enable && !captcha) {
        show({
          type: 'DANGER',
          message: translatedFields.enterValidCaptcha,
        });
        return;
      } else if (imageConfig && imageConfig.enable && !imageCaptchaId) {
        show({
          type: 'DANGER',
          message: translatedFields.tryRefreshCaptcha,
        });
        return;
      }

      if (username.length < 3 || username.length > 16) {
        show({
          type: 'DANGER',
          message: translatedFields.accountLengthSupport,
        });
        return;
      }

      if (password.length < 6 || password.length > 19) {
        show({
          type: 'DANGER',
          message: translatedFields.passwordLengthSupport,
        });
        return;
      }

      const body = await assemblyData();
      if (!Object.keys(body).length) {
        show({
          type: 'DANGER',
          message: translatedFields.loginFailed,
        });
        return;
      }
      await loginByUsernameMutation.mutateAsync({ data: body });
      setDisableLogin(true);

      show({
        message: translatedFields.loginCompleted,
        type: 'SUCCESS',
      });

      setTimeout(() => {
        show({
          message: translatedFields.redirecting,
          type: 'PRIMARY',
        });
      }, 1000);

      setTimeout(() => {
        const responseType =
          urlSearchParams?.get('response_type') ??
          urlSearchParams?.get('responseType') ??
          urlSearchParams?.get('response-type');
        const clientId =
          urlSearchParams?.get('client_id') ??
          urlSearchParams?.get('clientId') ??
          urlSearchParams?.get('client-id');
        const redirectUri =
          urlSearchParams?.get('redirect_uri') ??
          urlSearchParams?.get('redirectUri') ??
          urlSearchParams?.get('redirect-uri');
        const scope =
          urlSearchParams?.get('response_type') ??
          urlSearchParams?.get('responseType') ??
          urlSearchParams?.get('response-type');
        const state = urlSearchParams?.get('state');
        const params = {
          responseType,
          clientId,
          redirectUri,
          scope,
        } as any;

        if (state) {
          params.state = state;
        }

        if (responseType && clientId && redirectUri && scope) {
          location.href = `/oauth/auth?${new URLSearchParams(params)}`;
        } else {
          location.href = '/';
        }
      }, 1500);
    } catch (e) {
      loginByUsernameMutation.reset();
      show({
        type: 'DANGER',
        message: e,
      });
    }
  }

  async function assemblyData() {
    const { username, password, captcha } = form;
    const body = {
      username,
    } as any;
    if (data.imageConfig && data.imageConfig.enable && imageCaptchaId) {
      body.cid = imageCaptchaId.split('?')[0] || username;
      body.code = captcha;
    }
    const ePassword = await getEncryptedPassword(password);
    if (!ePassword) {
      show({
        type: 'DANGER',
        message: translatedFields.loginError,
      });
      return;
    }
    body.password = ePassword;
    return body;
  }

  function onErrorImage() {
    setImageLoadFailure(true);
    show({
      type: 'INFO',
      message: translatedFields.refreshCaptcha,
    });
  }

  async function onClickRefreshCaptcha() {
    try {
      const { username } = form;
      if (!username) {
        show({
          type: 'DANGER',
          message: translatedFields.enterAccountToRefresh,
        });
        return;
      }

      if (usernameCaptchaConfig.isClick) {
        if (usernameCaptchaConfig.countdown === 0) {
          return;
        }

        show({
          message: `${usernameCaptchaConfig.countdown} ${translatedFields.secondsToRetry}`,
          type: 'PRIMARY',
        });
        return;
      }

      if (usernameCaptchaConfig.total && usernameCaptchaConfig.total < 1) {
        show({
          type: 'DANGER',
          message: translatedFields.captchaLimitExceeded,
        });
        return;
      }

      const { id } = (await generateCaptchaByUsernameMutation.mutateAsync({
        data: {
          username,
        },
      })) as IGenerateCaptchaByUsername;
      setImageCaptchaId(id + `?${new Date().getTime()}`);

      if (usernameCaptchaConfig.interval) {
        let seconds = dayjs
          .duration(usernameCaptchaConfig.interval)
          .asSeconds();
        const countdownId = setInterval(() => {
          if (
            seconds === 0 &&
            typeof usernameCaptchaConfig.total !== 'undefined'
          ) {
            clearInterval(countdownId);
            setUsernameCaptchaConfig({
              ...usernameCaptchaConfig,
              isClick: false,
              total: usernameCaptchaConfig.total - 1,
              countdownId: undefined,
            });
          } else {
            seconds -= 1;
            setUsernameCaptchaConfig({
              ...usernameCaptchaConfig,
              countdown: seconds,
              isClick: true,
            });
          }
        }, 1000);
      }

      captchaInputRef.current?.focus();
      show({
        type: 'PRIMARY',
        message: translatedFields.refreshComplete,
      });
    } catch (e) {
      generateCaptchaByUsernameMutation.reset();
      show({
        type: 'DANGER',
        message: e,
      });
    }
  }

  async function getEncryptedPassword(password: string) {
    try {
      const publicKey = (await queryPasswordPublicKeyMutation.mutateAsync(
        {},
      )) as string;
      const jsEncrypt = await getJsEncrypt();
      jsEncrypt.setPublicKey(publicKey);
      return jsEncrypt.encrypt(password);
    } catch (e) {
      queryPasswordPublicKeyMutation.reset();
      show({
        type: 'DANGER',
        message: translatedFields.encryptPasswordFailed,
      });
    }
  }

  async function getJsEncrypt(): Promise<JSEncrypt> {
    let jsEncrypt;
    if (jsEncryptRef.current) {
      jsEncrypt = jsEncryptRef.current;
    } else {
      const JSEncrypt = (await import('jsencrypt')).JSEncrypt;
      jsEncrypt = new JSEncrypt();
      jsEncryptRef.current = jsEncrypt;
    }
    return jsEncrypt;
  }

  return (
    <form onSubmit={onSubmit}>
      <div className="my-3">
        <label className="form-label">
          <span className="text-danger fw-bold">*</span>
          {translatedFields.account}
        </label>
        <input
          required
          value={form.username}
          onChange={onChangeForm}
          name="username"
          type="text"
          className="form-control"
          placeholder={translatedFields.enterAccount}
          aria-describedby={translatedFields.enterAccount}
        />
      </div>

      <div className="my-3">
        <label className="form-label">
          <span className="text-danger fw-bold">*</span>
          {translatedFields.password}
        </label>
        <input
          required
          value={form.password}
          onChange={onChangeForm}
          name="password"
          type="password"
          placeholder={translatedFields.enterPassword}
          className="form-control"
          autoComplete="password"
        />
      </div>

      {data.imageConfig && data.imageConfig.enable && (
        <div className="my-3">
          <div className="d-flex align-items-center justify-content-between">
            <div>
              <label className="form-label">
                <span className="text-danger fw-bold">*</span>
                {translatedFields.captcha}
              </label>
            </div>
            <div>
              <a
                onClick={onClickRefreshCaptcha}
                className={classNames(
                  'text-decoration-none user-select-none',
                  usernameCaptchaConfig.countdown !== 0 ||
                    generateCaptchaByUsernameMutation.isLoading
                    ? 'link-secondary cursor-not-allowed'
                    : 'cursor-pointer',
                )}
              >
                {generateCaptchaByUsernameMutation.isLoading && (
                  <span
                    className="spinner-border spinner-border-sm me-2"
                    role="status"
                    aria-hidden="true"
                  ></span>
                )}
                {translatedFields.refresh}
                {usernameCaptchaConfig.countdown !== 0 &&
                  `(${usernameCaptchaConfig.countdown})`}
              </a>
            </div>
          </div>
          <div className="d-flex justify-content-between align-items-center">
            <div className="flex-grow-1 me-2">
              <input
                ref={captchaInputRef}
                required
                value={form.captcha}
                onChange={onChangeForm}
                name="captcha"
                type="text"
                placeholder={translatedFields.enterCaptcha}
                className="form-control"
              />
            </div>
            {data.imageConfig && data.imageConfig.enable && (
              <div
                className="ratio ratio-16x9"
                style={{
                  width: 100,
                  height: 56,
                  marginBottom: 14,
                }}
              >
                {(imageCaptchaId ||
                  (generateImageIdQuery.data &&
                    generateImageIdQuery.data.id)) &&
                !imageLoadFailure ? (
                  <Image
                    onClick={onClickRefreshCaptcha}
                    onError={onErrorImage}
                    alt={translatedFields.imageCaptcha}
                    title={translatedFields.refreshCaptchaButton}
                    className="cursor-pointer"
                    width={100}
                    height={56}
                    src={`/api/captcha/image/${
                      imageCaptchaId || generateImageIdQuery.data?.id
                    }`}
                    placeholder="blur"
                    blurDataURL={env.APP_BLUR_DATA_URL}
                  />
                ) : (
                  <div className="placeholder-glow">
                    <span
                      className="placeholder"
                      style={{ width: 100, height: 56 }}
                    ></span>
                  </div>
                )}
              </div>
            )}
          </div>
        </div>
      )}

      <button
        disabled={loginByUsernameMutation.isLoading || disableLogin}
        type="submit"
        className="btn btn-outline-primary mt-4 mb-3 w-100"
      >
        {loginByUsernameMutation.isLoading && (
          <span
            className="spinner-border spinner-border-sm me-2"
            role="status"
            aria-hidden="true"
          ></span>
        )}
        {disableLogin ? (
          <span>{translatedFields.loginCompleted}</span>
        ) : (
          <span>{translatedFields.loginPrompt}</span>
        )}
      </button>
    </form>
  );
}
